#!/usr/bin/python
# coding=utf-8

import os
import traceback

from addon_manager.addon_config import AddOnConfig
from addon_manager.addon_config_parser import AddOnCfgParseWrapper
from addon_manager.addon_port_mgr import AddOnPortMgr
from addon_manager.addon_logger import AddOnLogger
from addon_manager.addon_info import AddOnInfo, AddOnState
from addon_manager.addon_whitelist import AddOnWhitelist
from addon_manager.addon_exception import AddOnBaseException, RunningAddOnOpException

#
# 记录控制器内的所有安装的AddOn状态和信息
#


class AddOnStatus(object):
    __ADDON_STATUS_INST = None

    @staticmethod
    def inst():
        """
        config: global config, the instance of AddOnConfig
        """
        if AddOnStatus.__ADDON_STATUS_INST is None:
            AddOnStatus.__ADDON_STATUS_INST = AddOnStatus()

            config = AddOnConfig.inst()
            install_path = config.get_install_dir()
            if os.path.exists(install_path):
                AddOnStatus.__ADDON_STATUS_INST._load_exist_addon()
            else:
                os.mkdir(install_path)

        return AddOnStatus.__ADDON_STATUS_INST

    def __init__(self):
        self.addon_status_map = {}
        self.can_reload_from_disk = True

    def __inifile_path(self, basedir, addondir):
        config = AddOnConfig.inst()
        # todo：brycen AddOnConfig中增加一个iniconf_name  "addon.config.ini" 不与addonName绑定。
        # todo：这里优先使用"addon.config.ini"，如果找不到，再使用原来的方式。

        return os.path.join(basedir, addondir, addondir + config.get_iniconf_suffix())

    def _load_exist_addon(self):
        config = AddOnConfig.inst()
        install_path = config.get_install_dir()
        install_dirs = os.listdir(install_path)
        for addon_dir in install_dirs:
            try:
                ini_filepath = self.__inifile_path(install_path, addon_dir)
                if os.path.exists(ini_filepath):
                    self.regist(addon_dir)
            except RunningAddOnOpException:
                # 不输出错误frame
                pass
            except Exception:
                AddOnLogger().error(traceback.format_exc())

    def set_can_reload_from_disk(self, enable):
        """
        @param enable Bool
        """
        self.can_reload_from_disk = enable

    def _unload_nonexist_addon(self):
        config = AddOnConfig.inst()
        install_path = config.get_install_dir()
        addon_stat_inst = AddOnStatus.inst()
        addon_names = addon_stat_inst.keys()
        for addon_name in addon_names:
            ini_filepath = self.__inifile_path(install_path, addon_name)
            if not os.path.exists(ini_filepath):
                self.unregist(addon_name)

    def regist(self, addon_name):
        """
        @param addon_name: 位于config.get_install_dir() 下的 addon 安装目录
        """
        addon_info = self.addon_status_map.get(addon_name)
        if addon_info is not None and addon_info.state == AddOnState.RUNNING:
            raise RunningAddOnOpException()

        AddOnLogger().info("regist %s", addon_name)
        config = AddOnConfig.inst()
        addon_info = AddOnInfo(config=config, name=addon_name)
        try:
            # 从<addon_name>_config.ini解析配置
            cfg_parser = AddOnCfgParseWrapper(config)
            cfg_parser.parse_to(addon_info)
            # 为AddOn分配端口
            addon_port_mgr = AddOnPortMgr.get_inst(config)
            port = addon_port_mgr.allocate_port(addon_name)
            addon_info.set_iniconf("portal", port)
            # 回写addon配置到<addon_name>_config.ini
            cfg_parser.write_cfg_back(addon_info)
            # 生成自定义指令目录
            customprograms_dir = addon_info.get_customprograms_dir()
            if not os.path.exists(customprograms_dir):
                os.mkdir(customprograms_dir)
            addon_info.set_addon_state(AddOnState.STOPPED)
        except AddOnBaseException as e:
            log_msg = "load_exist_addon {}:{}[{}]".format(addon_name, e.msg(), hex(e.code()))
            AddOnLogger().error(log_msg)
            raise e
        finally:
            # 优化点：对字符串 addon_name 计算hash值
            self.addon_status_map[addon_name] = addon_info
        return addon_info

    def unregist(self, addon_name):
        """
        @param addon_name: addon 安装目录
        """
        AddOnLogger().info("unregist %s", addon_name)
        addon_info = self.addon_status_map.pop(addon_name, None)
        if addon_info is not None:
            config = AddOnConfig.inst()
            # 端口释放
            addon_port_mgr = AddOnPortMgr.get_inst(config)
            port = addon_port_mgr.release_port(addon_name)
            cfg_parser = AddOnCfgParseWrapper(config)
            cfg_parser.release_from(addon_info)

    def filter_addon(self, filter_func):
        """
        用于过滤出满足条件的AddOn
        @param filter_func(addoninfo), 返回Bool 是否满足
        """
        addonlist = []
        AddOnLogger().debug("typeof(self): %s , len of self.addon_status_map %s", type(self), len(self.addon_status_map))
        for addonpath in self.addon_status_map:
            addoninfo = self.addon_status_map[addonpath]
            AddOnLogger().info("addon_status_map %s", addoninfo)
            if filter_func(addoninfo):
                addonlist.append(addoninfo)
        return addonlist

    def get_addoninfo_by_name(self, addon_name):
        return self.addon_status_map.get(addon_name)

    def __str__(self):
        ret = ""
        for k in self.addon_status_map:
            ret += "{} : {} \n".format(k, self.addon_status_map[k])
        return ret

    def __len__(self):
        return len(self.addon_status_map)

    def __iter__(self):
        return iter(self.addon_status_map)

    def __next__(self):
        return next(self.addon_status_map)

    def __getitem__(self, addon_name):
        return self.addon_status_map[addon_name]

    def keys(self):
        return self.addon_status_map.keys()

    def check_if_addon_is_running(self, addon_name):
        addon_info = self.get_addoninfo_by_name(addon_name)
        if addon_info is None:
            return False
        return addon_info.state == AddOnState.RUNNING

    def change_addonstate(self, addon_name, state):
        config = AddOnConfig.inst()
        addon_port_mgr = AddOnPortMgr.get_inst(config)
        addon_info = self.get_addoninfo_by_name(addon_name)
        if state == AddOnState.STOPPED:
            addon_port_mgr.stop_use_port(addon_name)
        else:
            addon_port_mgr.start_use_port(addon_name)
        addon_info.set_addon_state(state)

    def get_addon_list(self):
        """
        获取AddOn配置信息列表
        """
        if self.can_reload_from_disk:
            # 重新从磁盘扫描addon
            self._unload_nonexist_addon()
            self._load_exist_addon()
        # def CompareTime(x, y):  #only python2
        #     stat_x = os.stat('/usr/etc/jkzuc/configs/JAKA/AddOns' + "/" + x)
        #     stat_y = os.stat('/usr/etc/jkzuc/configs/JAKA/AddOns' + "/" + y)
        #     if stat_x.st_ctime < stat_y.st_ctime:
        #         return -1
        #     elif stat_x.st_ctime > stat_y.st_ctime:
        #         return 1

        #     return 0

        # addondir = self.config.get_install_dir()
        # if not os.path.exists(addondir):
        #     os.mkdir(addondir)
        # list_files = os.listdir(addondir)
        # list_files.sort(CompareTime)

        addonlist = []
        addon_stat_inst = AddOnStatus.inst()
        addon_whitelist = AddOnWhitelist()
        for addon_name in addon_stat_inst:
            if not addon_whitelist.is_display_disable(addon_name):
                addon_info = addon_stat_inst[addon_name]
                ret_info = addon_info.get_iniconf()
                ret_info.update(
                    {
                        "isbuiltin": addon_whitelist.is_builtin_addon(addon_name),
                        "perm_delete": not addon_whitelist.is_delete_disable(addon_name),
                        "perm_install": not addon_whitelist.is_update_disable(addon_name),
                        "perm_export": not addon_whitelist.is_export_disable(addon_name),
                    }
                )
                addonlist.append(ret_info)
        # print('addonlist is {}'.format(addonlist))
        return addonlist


if __name__ == "__main__":
    # ast = AddOnStatus()
    # ast.regist('a', {'st': 'error'})
    # print(str(ast))
    # ast.unregist('b')
    # print(str(ast))
    # print(len(ast))
    # ast.unregist('a')
    # print(len(ast))
    pass
